﻿using ICSharpCode.SharpZipLib.Zip;
using System;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
namespace CommonLib
{
	public static class SharpZipUnit
	{
		static readonly int Level = 6;
		#region 压缩
		public static bool Zip(string[] fileArry, string destinationFile, string password = null, ZipActionCallback callBack = null)
		{

			if (null == fileArry || string.IsNullOrEmpty(destinationFile))
			{
				callBack.OnFinishAction(false);
				return false;
			}
			//开启一个输出Zip输出流
			using (ZipOutputStream zoStream = new ZipOutputStream(File.Create(destinationFile)))
			{
				zoStream.SetLevel(Level);
				if (null != password)
					zoStream.Password = password;

				for (int i = 0; i < fileArry.Length; i++)
				{
					bool success = false;
					string fileName = fileArry[i];
					if (Directory.Exists(fileName))
						success = ZipDirectory(fileName, string.Empty, zoStream, callBack);
					else if (File.Exists(fileName))
						success = ZipFile(fileName, string.Empty, zoStream, callBack);

					if (!success)
					{
						callBack?.OnFinishAction(false);
						return false;
					}
				}
			}
			////关闭输出流
			//zoStream.Finish();
			//zoStream.Close();

			callBack?.OnFinishAction(true);
			return true;
		}
		public static async void ZipSyncAsync(string[] fileArry, string destinationFile, string password = null, ZipActionCallback callBack = null)
		{
			bool result = await ZipSyncAction(fileArry, destinationFile, password, callBack);
		}
		static async Task<bool> ZipSyncAction(string[] fileArry, string destinationFile, string password = null, ZipActionCallback callBack = null)
		{
			if (null == fileArry || string.IsNullOrEmpty(destinationFile))
			{
				callBack.OnFinishAction(false);
				return false;
			}

			//开启一个输出Zip输出流
			using (ZipOutputStream zoStream = new ZipOutputStream(File.Create(destinationFile)))
			{
				zoStream.SetLevel(Level);
				if (null != password)
					zoStream.Password = password;

				for (int i = 0; i < fileArry.Length; i++)
				{
					bool success = false;
					string fileName = fileArry[i];
					if (Directory.Exists(fileName))
						success = await ZipDirectorySync(fileName, string.Empty, zoStream, callBack);
					else if (File.Exists(fileName))
						success = await ZipFileSync(fileName, string.Empty, zoStream, callBack);

					if (!success)
					{
						callBack?.OnFinishAction(false);
						return false;
					}
				}
			}

			////关闭输出流
			//zoStream.Finish();
			//zoStream.Close();


			callBack?.OnFinishAction(true);
			return true;
		}

		public static bool ZipFile(string sourceFileName, string relativePath, ZipOutputStream zoStream, ZipActionCallback callback = null)
		{
			ZipEntry entry = null;
			FileStream filestream = null;
			try
			{
				string entryName = new System.Text.StringBuilder(relativePath).Append("/").Append(Path.GetFileName(sourceFileName)).ToString();
				entry = new ZipEntry(entryName);
				entry.DateTime = System.DateTime.Now;

				if ((null != callback) && !callback.OnPreAction(entry))
					return true;

				//读取文件信息 
				filestream = File.OpenRead(sourceFileName);
				byte[] buffer = new byte[filestream.Length];
				filestream.Read(buffer, 0, buffer.Length);
				filestream.Close();
				entry.Size = buffer.Length;
				//将文件写入Zip
				zoStream.PutNextEntry(entry);
				zoStream.Write(buffer, 0, buffer.Length);

			}
			catch (System.Exception e)
			{
				Debug.LogError(e.StackTrace);
				return false;
			}
			finally
			{
				//结束释放文件流
				if (null != filestream)
				{
					filestream.Close();
					filestream.Dispose();
				}
			}
			callback?.OnSingleAction(entry);
			return true;
		}
		public static async Task<bool> ZipFileSync(string sourceFileName, string relativePath, ZipOutputStream zoStream, ZipActionCallback callback = null)
		{
			ZipEntry entry = null;
			FileStream filestream = null;
			try
			{
				string entryName = new System.Text.StringBuilder(relativePath).Append("/").Append(Path.GetFileName(sourceFileName)).ToString();
				entry = new ZipEntry(entryName);
				entry.DateTime = System.DateTime.Now;

				if ((null != callback) && !callback.OnPreAction(entry))
					return true;

				//读取文件信息 
				filestream = File.OpenRead(sourceFileName);
				byte[] buffer = new byte[filestream.Length];

				await Task.Run(() =>
				{
					filestream.Read(buffer, 0, buffer.Length);
					filestream.Close();
				});
				//await filestream.ReadAsync(buffer, 0, buffer.Length);
				//filestream.Close();

				entry.Size = buffer.Length;
				//将文件写入Zip
				await Task.Run(() =>
				{
					zoStream.PutNextEntry(entry);
					zoStream.Write(buffer, 0, buffer.Length);
				});
				//从ZipDirectorySync线程出来的套线程要新开一个
				#region  Bug	
				//zoStream.PutNextEntry(entry);
				//await zoStream.WriteAsync(buffer, 0, buffer.Length);
				#endregion
			}
			catch (System.Exception e)
			{
				Debug.LogError(e.StackTrace.ToString());

				return false;
			}
			finally
			{
				//结束释放文件流
				if (null != filestream)
				{
					filestream.Close();
					filestream.Dispose();
				}
			}
			callback?.OnSingleAction(entry);
			return true;
		}

		public static bool ZipDirectory(string sourceDirectoryName, string relativePath, ZipOutputStream zoStream, ZipActionCallback callback = null)
		{
			ZipEntry entry = null;
			try
			{
				string entryName = Path.Combine(relativePath, Path.GetFileName(sourceDirectoryName) + '/');
				entry = new ZipEntry(entryName);
				entry.DateTime = System.DateTime.Now;
				entry.Size = 0;

				if (null != callback && !callback.OnPreAction(entry))
					return true;

				zoStream.PutNextEntry(entry);
				zoStream.Flush();

				string[] files = Directory.GetFiles(sourceDirectoryName);
				for (int i = 0; i < files.Length; i++)
				{
					if (!ZipFile(files[i], Path.Combine(relativePath, Path.GetFileName(sourceDirectoryName)), zoStream, callback))
						return false;
				}

				string[] directories = Directory.GetDirectories(sourceDirectoryName);
				for (int j = 0; j < directories.Length; j++)
				{
					if (!ZipDirectory(directories[j], Path.Combine(relativePath, Path.GetFileName(sourceDirectoryName)), zoStream, callback))
						return false;
				}

			}
			catch (System.Exception e)
			{
				Debug.LogError(e.StackTrace);
				return false;
			}



			callback?.OnSingleAction(entry);
			return true;
		}
		public static async Task<bool> ZipDirectorySync(string sourceDirectoryName, string relativePath, ZipOutputStream zoStream, ZipActionCallback callback = null)
		{
			ZipEntry entry = null;
			try
			{
				string entryName = Path.Combine(relativePath, Path.GetFileName(sourceDirectoryName) + '/');
				entry = new ZipEntry(entryName);
				entry.DateTime = System.DateTime.Now;
				entry.Size = 0;

				if (null != callback && !callback.OnPreAction(entry))
					return true;

				zoStream.PutNextEntry(entry);
				await zoStream.FlushAsync();

				string[] files = Directory.GetFiles(sourceDirectoryName);
				for (int i = 0; i < files.Length; i++)
				{
					if (!await ZipFileSync(files[i], Path.Combine(relativePath, Path.GetFileName(sourceDirectoryName)), zoStream, callback))
						return false;
				}

				string[] directories = Directory.GetDirectories(sourceDirectoryName);
				for (int j = 0; j < directories.Length; j++)
				{
					if (!await ZipDirectorySync(directories[j], Path.Combine(relativePath, Path.GetFileName(sourceDirectoryName)), zoStream, callback))
						return false;
				}

			}
			catch (System.Exception e)
			{
				Debug.LogError(e.StackTrace);
				return false;
			}



			callback?.OnSingleAction(entry);
			return true;
		}
		#endregion

		#region 解压缩
		public static bool UnZip(string fileName, string destinationFile, string password = null, ZipActionCallback callback = null)
		{
			if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(destinationFile))
			{
				callback?.OnFinishAction(false);
				return false;
			}
			try
			{
				return UnZip(File.Open(fileName, FileMode.Open, FileAccess.Read), destinationFile, password, callback);
			}
			catch (System.Exception e)
			{
				Debug.LogError(e.StackTrace);
				callback?.OnFinishAction(false);
				return false;
			}
		}
		static async Task<bool> UnZipSyncWork(string fileName, string destinationFile, string password = null, ZipActionCallback callback = null)
		{
			if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(destinationFile))
			{
				callback?.OnFinishAction(false);
				return false;
			}
			try
			{
				return await UnZipSyncWork(File.Open(fileName, FileMode.Open, FileAccess.Read), destinationFile, password, callback);
			}
			catch (System.Exception e)
			{
				Debug.LogError(e.StackTrace);
				callback?.OnFinishAction(false);
				return false;
			}
		}
		public static bool UnZip(byte[] bytes, string destinationFile, string password = null, ZipActionCallback callback = null)
		{
			if (null == bytes || string.IsNullOrEmpty(destinationFile))
			{
				callback?.OnFinishAction(false);
				return false;
			}

			bool success = UnZip(new MemoryStream(bytes), destinationFile, password, callback);

			if (!success)
			{
				callback?.OnFinishAction(false);
			}
			return success;

		}
		static async Task<bool> UnZipSyncWork(byte[] bytes, string destinationFile, string password = null, ZipActionCallback callback = null)
		{
			if (null == bytes || string.IsNullOrEmpty(destinationFile))
			{
				callback?.OnFinishAction(false);
				return false;
			}

			bool success = await UnZipSyncWork(new MemoryStream(bytes), destinationFile, password, callback);

			if (!success)
			{
				callback?.OnFinishAction(false);
			}
			return success;

		}
		public static bool UnZip(Stream stream, string destinationFile, string password = null, ZipActionCallback callback = null)
		{

			if (null == stream || string.IsNullOrEmpty(destinationFile))
			{
				callback?.OnFinishAction(false);
				return false;
			}

			if (!Directory.Exists(destinationFile))
				Directory.CreateDirectory(destinationFile);

			ZipEntry entry = null;
			using (ZipInputStream ziStream = new ZipInputStream(stream))
			{
				if (!string.IsNullOrEmpty(password))
					ziStream.Password = password;

				while (null != (entry = ziStream.GetNextEntry()))
				{
					if (string.IsNullOrEmpty(entry.Name))
						continue;
					if (null != callback && !callback.OnPreAction(entry))
						continue;
					string fileFullName = Path.Combine(destinationFile, entry.Name);
					if (entry.IsDirectory)
					{
						Directory.CreateDirectory(fileFullName);
						continue;
					}

					try
					{

						using (FileStream fs = File.Create(fileFullName))
						{
							byte[] bytes = new byte[1024];
							while (true)
							{
								int count = ziStream.Read(bytes, 0, 1024);
								if (count > 0)
									fs.Write(bytes, 0, count);
								else
								{
									callback?.OnSingleAction(entry);
									break;
								}
							}

						}

					}
					catch (System.Exception e)
					{
						Debug.LogError(e.StackTrace);
						callback?.OnFinishAction(false);
						return false;
					}

				}
			}

			callback?.OnFinishAction(true);
			return true;
		}
		static async Task<bool> UnZipSyncWork(Stream stream, string destinationFile, string password = null, ZipActionCallback callback = null)
		{

			if (null == stream || string.IsNullOrEmpty(destinationFile))
			{
				callback?.OnFinishAction(false);
				return false;
			}

			if (!Directory.Exists(destinationFile))
				Directory.CreateDirectory(destinationFile);
			bool success = await Task.Run(() =>
		   {
			   ZipEntry entry = null;
			   using (ZipInputStream ziStream = new ZipInputStream(stream))
			   {
				   if (!string.IsNullOrEmpty(password))
					   ziStream.Password = password;

				   while (null != (entry = ziStream.GetNextEntry()))
				   {
					   if (string.IsNullOrEmpty(entry.Name))
						   continue;
					   if (null != callback && !callback.OnPreAction(entry))
						   continue;
					   string fileFullName = Path.Combine(destinationFile, entry.Name);
					   if (entry.IsDirectory)
					   {
						   Directory.CreateDirectory(fileFullName);
						   continue;
					   }

					   try
					   {

						   using (FileStream fs = File.Create(fileFullName))
						   {
							   byte[] bytes = new byte[1024];
							   while (true)
							   {
								   int count = ziStream.Read(bytes, 0, 1024);
								   if (count > 0)
									   fs.Write(bytes, 0, count);
								   else
								   {
									   callback?.OnSingleAction(entry);
									   break;
								   }
							   }
						   }
					   }
					   catch (System.Exception e)
					   {
						   Debug.LogError(e.StackTrace);
						//callback?.OnFinishAction(false);
						return false;
					   }
				   }

				//callback?.OnFinishAction(true);
				return true;
			   }
		   });
			if (success)
				callback?.OnFinishAction(true);
			else
				callback?.OnFinishAction(false);
			return success;
		}



		public static async void UnZipSync(string fileName, string destinationFile, string password = null, ZipActionCallback callback = null)
		{
			bool success = await UnZipSyncWork(fileName, destinationFile, password, callback);
		}
		public static async void UnZipSync(byte[] bytes, string destinationFile, string password = null, ZipActionCallback callback = null)
		{
			bool success = await UnZipSyncWork(bytes, destinationFile, password, callback);
		}
		public static async void UnZipSync(Stream stream, string destinationFile, string password = null, ZipActionCallback callback = null)
		{
			bool success = await UnZipSyncWork(stream, destinationFile, password, callback);
		}
		#endregion
		public static long GetDirectoryLength(string dirPath)
		{
			long len = 0;
			//判断该路径是否存在（是否为文件夹）
			if (!Directory.Exists(dirPath))
			{
				//查询文件的大小
				len = FileSize(dirPath);
			}
			else
			{
				//定义一个DirectoryInfo对象
				DirectoryInfo di = new DirectoryInfo(dirPath);

				//通过GetFiles方法，获取di目录中的所有文件的大小
				foreach (FileInfo fi in di.GetFiles())
				{
					len += fi.Length;
				}
				//获取di中所有的文件夹，并存到一个新的对象数组中，以进行递归
				DirectoryInfo[] dis = di.GetDirectories();
				if (dis.Length > 0)
				{
					for (int i = 0; i < dis.Length; i++)
					{
						len += GetDirectoryLength(dis[i].FullName);
					}
				}
			}
			return len;
		}
		public static long FileSize(string path)
		{
			//定义一个FileInfo对象，是指与filePath所指向的文件相关联，以获取其大小
			FileInfo fileInfo = new FileInfo(path);
			return fileInfo.Length;
		}
	}

	public class ZipActionCallback
	{

		/// <summary>用于进行压缩前的处理</summary>
		public Func<ZipEntry, bool> PreAction = null;
		/// <summary>用于单个文件或者文件夹进行处理后</summary>
		public Action<ZipEntry> SingleAction = null;
		/// <summary>在压缩进行时出现的结束处理</summary>
		public Action<bool> FinishAction = null;
		public ZipActionCallback() { }

		public ZipActionCallback(Func<ZipEntry, bool> pre, Action<ZipEntry> single, Action<bool> finish)
		{
			this.PreAction = pre;
			this.SingleAction = single;
			this.FinishAction = finish;
		}


		/// <summary>
		/// 在进行压缩前执行调用
		/// </summary>
		/// <param name="entry"></param>
		/// <returns>用于筛选压缩文件</returns>
		public virtual bool OnPreAction(ZipEntry entry)
		{
			if (null != PreAction)
				return PreAction.Invoke(entry);
			else
				return true;
		}

		/// <summary>
		/// 在执行压缩后执行调用
		/// </summary>
		/// <param name="entry"></param>
		public virtual void OnSingleAction(ZipEntry entry)
		{
			SingleAction?.Invoke(entry);
		}
		/// <summary>
		/// 在执行压缩全部完成后
		/// </summary>
		/// <param name="success"></param>
		public virtual void OnFinishAction(bool success)
		{
			FinishAction?.Invoke(success);
		}
	}
}